1   /*
2    * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions
6    * are met:
7    *
8    *   - Redistributions of source code must retain the above copyright
9    *     notice, this list of conditions and the following disclaimer.
10   *
11   *   - Redistributions in binary form must reproduce the above copyright
12   *     notice, this list of conditions and the following disclaimer in the
13   *     documentation and/or other materials provided with the distribution.
14   *
15   *   - Neither the name of Oracle nor the names of its
16   *     contributors may be used to endorse or promote products derived
17   *     from this software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  
32  /*
33   */
34  
35  import java.awt.*;
36  import java.awt.event.*;
37  import java.awt.image.BufferedImage;
38  import java.awt.geom.Line2D;
39  import java.awt.geom.Rectangle2D;
40  import java.util.Date;
41  import javax.swing.*;
42  import javax.swing.border.EtchedBorder;
43  import javax.swing.border.TitledBorder;
44  import java.lang.management.*;
45  /**
46   * Demo code which plots the memory usage by all memory pools.
47   * The memory usage is sampled at some time interval using
48   * java.lang.management API. This demo code is modified based
49   * java2d MemoryMonitor demo.
50   */
51  public class MemoryMonitor extends JPanel {
52  
53      private static final long serialVersionUID = -3463003810776195761L;
54      static JCheckBox dateStampCB = new JCheckBox("Output Date Stamp");
55      public Surface surf;
56      JPanel controls;
57      boolean doControls;
58      JTextField tf;
59      // Get memory pools.
60      static java.util.List<MemoryPoolMXBean> mpools =
61          ManagementFactory.getMemoryPoolMXBeans();
62      // Total number of memory pools.
63      static int numPools = mpools.size();
64  
65      public MemoryMonitor() {
66          setLayout(new BorderLayout());
67          setBorder(new TitledBorder(new EtchedBorder(), "Memory Monitor"));
68          add(surf = new Surface());
69          controls = new JPanel();
70          controls.setPreferredSize(new Dimension(135,80));
71          Font font = new Font("serif", Font.PLAIN, 10);
72          JLabel label = new JLabel("Sample Rate");
73          label.setFont(font);
74          label.setForeground(Color.red);
75          controls.add(label);
76          tf = new JTextField("1000");
77          tf.setPreferredSize(new Dimension(45,20));
78          controls.add(tf);
79          controls.add(label = new JLabel("ms"));
80          label.setFont(font);
81          label.setForeground(Color.red);
82          controls.add(dateStampCB);
83          dateStampCB.setFont(font);
84          addMouseListener(new MouseAdapter() {
85              @Override
86              public void mouseClicked(MouseEvent e) {
87                 removeAll();
88                 if ((doControls = !doControls)) {
89                     surf.stop();
90                     add(controls);
91                 } else {
92                     try {
93                         surf.sleepAmount = Long.parseLong(tf.getText().trim());
94                     } catch (Exception ex) {}
95                     surf.start();
96                     add(surf);
97                 }
98                 validate();
99                 repaint();
100             }
101         });
102     }
103 
104 
105     public class Surface extends JPanel implements Runnable {
106 
107         public Thread thread;
108         public long sleepAmount = 1000;
109         public int  usageHistCount = 20000;
110         private int w, h;
111         private BufferedImage bimg;
112         private Graphics2D big;
113         private Font font = new Font("Times New Roman", Font.PLAIN, 11);
114         private int columnInc;
115         private float usedMem[][];
116         private int ptNum[];
117         private int ascent, descent;
118         private Rectangle graphOutlineRect = new Rectangle();
119         private Rectangle2D mfRect = new Rectangle2D.Float();
120         private Rectangle2D muRect = new Rectangle2D.Float();
121         private Line2D graphLine = new Line2D.Float();
122         private Color graphColor = new Color(46, 139, 87);
123         private Color mfColor = new Color(0, 100, 0);
124         private String usedStr;
125 
126 
127         public Surface() {
128             setBackground(Color.black);
129             addMouseListener(new MouseAdapter() {
130                 @Override
131                 public void mouseClicked(MouseEvent e) {
132                     if (thread == null) start(); else stop();
133                 }
134             });
135             usedMem = new float[numPools][];
136             ptNum = new int[numPools];
137         }
138 
139         @Override
140         public Dimension getMinimumSize() {
141             return getPreferredSize();
142         }
143 
144         @Override
145         public Dimension getMaximumSize() {
146             return getPreferredSize();
147         }
148 
149         @Override
150         public Dimension getPreferredSize() {
151             return new Dimension(135,80);
152         }
153 
154 
155         @Override
156         public void paint(Graphics g) {
157 
158             if (big == null) {
159                 return;
160             }
161 
162             big.setBackground(getBackground());
163             big.clearRect(0,0,w,h);
164 
165 
166             h = h / ((numPools + numPools%2) / 2);
167             w = w / 2;
168 
169             int k=0; // index of memory pool.
170             for (int i=0; i < 2;i++) {
171                for (int j=0; j < (numPools + numPools%2)/ 2; j++) {
172                  plotMemoryUsage(w*i,h*j,w,h,k);
173                  if (++k >= numPools) {
174                     i = 3;
175                     j = (numPools + numPools%2)/ 2;
176                     break;
177                  }
178                }
179             }
180             g.drawImage(bimg, 0, 0, this);
181         }
182 
183         public void plotMemoryUsage(int x1, int y1, int x2, int y2, int npool) {
184 
185             MemoryPoolMXBean mp = mpools.get(npool);
186             float usedMemory =  mp.getUsage().getUsed();
187             float totalMemory =  mp.getUsage().getMax();
188 
189             // .. Draw allocated and used strings ..
190             big.setColor(Color.green);
191 
192             // Print Max memory allocated for this memory pool.
193             big.drawString(String.valueOf((int)totalMemory/1024) + "K Max ", x1+4.0f, (float) y1 + ascent+0.5f);
194             big.setColor(Color.yellow);
195 
196             // Print the memory pool name.
197             big.drawString(mp.getName(),  x1+x2/2, (float) y1 + ascent+0.5f);
198 
199             // Print the memory used by this memory pool.
200             usedStr = String.valueOf((int)usedMemory/1024)
201                 + "K used";
202             big.setColor(Color.green);
203             big.drawString(usedStr, x1+4, y1+y2-descent);
204 
205             // Calculate remaining size
206             float ssH = ascent + descent;
207             float remainingHeight = (float) (y2 - (ssH*2) - 0.5f);
208             float blockHeight = remainingHeight/10;
209             float blockWidth = 20.0f;
210             float remainingWidth = (float) (x2 - blockWidth - 10);
211 
212             // .. Memory Free ..
213             big.setColor(mfColor);
214             int MemUsage = (int) (((totalMemory - usedMemory) / totalMemory) * 10);
215             int i = 0;
216             for ( ; i < MemUsage ; i++) {
217                 mfRect.setRect(x1+5,(float) y1+ssH+i*blockHeight,
218                                 blockWidth,(float) blockHeight-1);
219                 big.fill(mfRect);
220             }
221 
222             // .. Memory Used ..
223             big.setColor(Color.green);
224             for ( ; i < 10; i++)  {
225                 muRect.setRect(x1+5,(float) y1 + ssH+i*blockHeight,
226                                 blockWidth,(float) blockHeight-1);
227                 big.fill(muRect);
228             }
229 
230             // .. Draw History Graph ..
231             if (remainingWidth <= 30) remainingWidth = (float)30;
232             if (remainingHeight <= ssH) remainingHeight = (float)ssH;
233             big.setColor(graphColor);
234             int graphX = x1+30;
235             int graphY = y1 + (int) ssH;
236             int graphW = (int) remainingWidth;
237             int graphH = (int) remainingHeight;
238 
239             graphOutlineRect.setRect(graphX, graphY, graphW, graphH);
240             big.draw(graphOutlineRect);
241 
242             int graphRow = graphH/10;
243 
244             // .. Draw row ..
245             for (int j = graphY; j <= graphH+graphY; j += graphRow) {
246                 graphLine.setLine(graphX,j,graphX+graphW,j);
247                 big.draw(graphLine);
248             }
249 
250             // .. Draw animated column movement ..
251             int graphColumn = graphW/15;
252 
253             if (columnInc == 0) {
254                 columnInc = graphColumn;
255             }
256 
257             for (int j = graphX+columnInc; j < graphW+graphX; j+=graphColumn) {
258                 graphLine.setLine(j,graphY,j,graphY+graphH);
259                 big.draw(graphLine);
260             }
261 
262             --columnInc;
263 
264             // Plot memory usage by this memory pool.
265             if (usedMem[npool] == null) {
266                 usedMem[npool] = new float[usageHistCount];
267                 ptNum[npool] = 0;
268             }
269 
270             // save memory usage history.
271             usedMem[npool][ptNum[npool]] = usedMemory;
272 
273             big.setColor(Color.yellow);
274 
275             int w1; // width of memory usage history.
276             if (ptNum[npool] > graphW) {
277                 w1 = graphW;
278             } else {
279                 w1 = ptNum[npool];
280             }
281 
282 
283             for (int j=graphX+graphW-w1, k=ptNum[npool]-w1; k < ptNum[npool];
284                                                                 k++, j++) {
285                  if (k != 0) {
286                      if (usedMem[npool][k] != usedMem[npool][k-1]) {
287                          int h1 = (int)(graphY + graphH * ((totalMemory -usedMem[npool][k-1])/totalMemory));
288                          int h2 = (int)(graphY + graphH * ((totalMemory -usedMem[npool][k])/totalMemory));
289                          big.drawLine(j-1, h1, j, h2);
290                      } else {
291                          int h1 = (int)(graphY + graphH * ((totalMemory -usedMem[npool][k])/totalMemory));
292                          big.fillRect(j, h1, 1, 1);
293                      }
294                  }
295             }
296             if (ptNum[npool]+2 == usedMem[npool].length) {
297                 // throw out oldest point
298                 for (int j = 1;j < ptNum[npool]; j++) {
299                      usedMem[npool][j-1] = usedMem[npool][j];
300                 }
301                 --ptNum[npool];
302             } else {
303                 ptNum[npool]++;
304             }
305         }
306 
307 
308         public void start() {
309             thread = new Thread(this);
310             thread.setPriority(Thread.MIN_PRIORITY);
311             thread.setName("MemoryMonitor");
312             thread.start();
313         }
314 
315 
316         public synchronized void stop() {
317             thread = null;
318             notify();
319         }
320 
321         @Override
322         public void run() {
323 
324             Thread me = Thread.currentThread();
325 
326             while (thread == me && !isShowing() || getSize().width == 0) {
327                 try {
328                     Thread.sleep(500);
329                 } catch (InterruptedException e) { return; }
330             }
331 
332             while (thread == me && isShowing()) {
333                 Dimension d = getSize();
334                 if (d.width != w || d.height != h) {
335                     w = d.width;
336                     h = d.height;
337                     bimg = (BufferedImage) createImage(w, h);
338                     big = bimg.createGraphics();
339                     big.setFont(font);
340                     FontMetrics fm = big.getFontMetrics(font);
341                     ascent = (int) fm.getAscent();
342                     descent = (int) fm.getDescent();
343                 }
344                 repaint();
345                 try {
346                     Thread.sleep(sleepAmount);
347                 } catch (InterruptedException e) { break; }
348                 if (MemoryMonitor.dateStampCB.isSelected()) {
349                      System.out.println(new Date().toString() + " " + usedStr);
350                 }
351             }
352             thread = null;
353         }
354     }
355 
356 
357     // Test thread to consume memory
358     static class Memeater extends ClassLoader implements Runnable {
359         Object y[];
360         public Memeater() {}
361         @Override
362         public void run() {
363             y = new Object[10000000];
364             int k =0;
365             while(true) {
366                  if (k == 5000000) k=0;
367                  y[k++] = new Object();
368                  try {
369                      Thread.sleep(20);
370                  } catch (Exception x){}
371 
372                  // to consume perm gen storage
373                  try {
374                      // the classes are small so we load 10 at a time
375                      for (int i=0; i<10; i++) {
376                         loadNext();
377                      }
378                  } catch (ClassNotFoundException x) {
379                    // ignore exception
380                  }
381 
382            }
383 
384         }
385 
386         Class<?> loadNext() throws ClassNotFoundException {
387 
388             // public class TestNNNNNN extends java.lang.Object{
389             // public TestNNNNNN();
390             //   Code:
391             //    0:    aload_0
392             //    1:    invokespecial   #1; //Method java/lang/Object."<init>":()V
393             //    4:    return
394             // }
395 
396             int begin[] = {
397                 0xca, 0xfe, 0xba, 0xbe, 0x00, 0x00, 0x00, 0x30,
398                 0x00, 0x0a, 0x0a, 0x00, 0x03, 0x00, 0x07, 0x07,
399                 0x00, 0x08, 0x07, 0x00, 0x09, 0x01, 0x00, 0x06,
400                 0x3c, 0x69, 0x6e, 0x69, 0x74, 0x3e, 0x01, 0x00,
401                 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43,
402                 0x6f, 0x64, 0x65, 0x0c, 0x00, 0x04, 0x00, 0x05,
403                 0x01, 0x00, 0x0a, 0x54, 0x65, 0x73, 0x74 };
404 
405             int end [] = {
406                 0x01, 0x00, 0x10,
407                 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e,
408                 0x67, 0x2f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
409                 0x00, 0x21, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00,
410                 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04,
411                 0x00, 0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00,
412                 0x00, 0x11, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
413                 0x00, 0x05, 0x2a, 0xb7, 0x00, 0x01, 0xb1, 0x00,
414                 0x00, 0x00, 0x00, 0x00, 0x00 };
415 
416 
417             // TestNNNNNN
418 
419             String name = "Test" + Integer.toString(count++);
420 
421             byte value[];
422             try {
423                 value = name.substring(4).getBytes("UTF-8");
424             } catch (java.io.UnsupportedEncodingException x) {
425                 throw new Error();
426             }
427 
428             // construct class file
429 
430             int len = begin.length + value.length + end.length;
431             byte b[] = new byte[len];
432             int pos=0;
433             for (int i: begin) {
434                 b[pos++] = (byte) i;
435             }
436             for (byte v: value) {
437                 b[pos++] = v;
438             }
439             for (int e: end) {
440                 b[pos++] = (byte) e;
441             }
442 
443             return defineClass(name, b, 0, b.length);
444 
445         }
446         static int count = 100000;
447 
448     }
449 
450     public static void main(String s[]) {
451         final MemoryMonitor demo = new MemoryMonitor();
452         WindowListener l = new WindowAdapter() {
453             @Override
454             public void windowClosing(WindowEvent e) {System.exit(0);}
455             @Override
456             public void windowDeiconified(WindowEvent e) { demo.surf.start(); }
457             @Override
458             public void windowIconified(WindowEvent e) { demo.surf.stop(); }
459         };
460         JFrame f = new JFrame("MemoryMonitor");
461         f.addWindowListener(l);
462         f.getContentPane().add("Center", demo);
463         f.pack();
464         f.setSize(new Dimension(400,500));
465         f.setVisible(true);
466         demo.surf.start();
467         Thread thr = new Thread(new Memeater());
468         thr.start();
469     }
470 
471 }